import React, { Component, ReactNode, ReactElement } from 'react';
import ReactDom from 'react-dom';
import './index.less';

let mousePosition: { x: number; y: number } | null;

const getClickPosition = (evt: React.MouseEvent<HTMLElement>) => {
  mousePosition = {
    x: evt.pageX,
    y: evt.pageY
  };
  console.log(
    `获取到点击的位置___  x: ${mousePosition.x} -- y: ${mousePosition.y}`
  );

  // 100ms以内点击，则从点击位置开始
  // 否则认为缩放处理
  setTimeout(() => {
    mousePosition = null;
  }, 100);
};
export interface IModalProps {
  oneclstag?: string; // 第一个按钮埋点
  twoclstag?: string; // 第二个按钮埋点
  errorCode?: number; // 错误码
  visible: boolean; // 弹窗是否显示
  header?: React.ReactNode | boolean;
  footer?: React.ReactNode | boolean;
  children:
    | React.ReactElement<any>[]
    | React.ReactChild
    | React.ReactChildren
    | React.FC; // 弹窗内容
  title?: string; // 弹窗标题
  okText?: ReactNode | ReactElement | string; // 弹窗确定文案
  cancelText?: ReactNode | ReactElement | string; // 弹窗取消文案
  wrapClassName?: string; // 自定义类名
  style?: {}; // 自定义样式
  onOk?: (evt: React.MouseEvent<HTMLElement>) => void; // 弹窗确定事件
  onCancel?: (evt: React.MouseEvent<HTMLElement>) => void; // 弹窗取消事件
  afterClose?: (val?: any) => void; // 关闭了弹窗执行回调函数
  cancelRTBtn?: boolean | undefined;
}

export interface IModalState {
  visible?: boolean;
}

// 弹窗默认文案
export interface IModalLocal {
  cancelText: string;
  okText: string;
}

// BasicModal弹窗基础高阶组件作用于处理逻辑， Dialog展示组件，只作用于展示，不处理任何逻辑
class BasicModal extends Component<IModalProps, IModalState> {
  static defaultProps = {
    width: 520,
    transitionName: 'zoom',
    maskTransitionName: 'fade',
    confirmLoading: false,
    visible: false,
    cancelRTBtn: true,
    title: ''
  };

  constructor(props: IModalProps) {
    super(props);
    this.state = {};
  }

  // 需要谨慎处理
  shouldComponentUpdate(nextProps: IModalProps, nextState: IModalState) {
    // 只当弹窗显示状态改变时，才重新渲染组件，否则不渲染，
    // 避免一个页面使用了多个BasicModal导致影响其他组件走afterClose流程
    return nextProps.visible !== this.props.visible;
  }

  public getSnapshotBeforeUpdate(preProps: IModalProps, preState: IModalState) {
    if (!this.props.visible) {
      // 状态更新完毕之后，再调用判断是否还存在Modal，解决多Modal存在的问题
      setTimeout(() => {
        !document.querySelector('.modal') && this.looseBody();
      }, 0);
      return true;
    } else {
      this.fixedBody();
      return false;
    }
  }
  componentDidMount() {
    if (this.props.visible) {
      this.fixedBody();
    }
  }

  componentWillUnmount() {
    setTimeout(() => {
      // popup 弹起之后 继续弹错误弹窗可能会导致关闭错误弹窗后，popup的滚动穿透scrollTop位置失效 - 优化
      !document.querySelector('.mask-open') &&
        !document.querySelector('.modal') &&
        this.looseBody();
    }, 0);
  }
  public componentDidUpdate(
    preProps: IModalProps,
    preState: IModalState,
    snapShot: any
  ) {
    if (snapShot) {
      // 子组件执行完毕父组件才会更新状态导致弹窗渲染关闭之前执行afterClose，这不是我们想要的效果
      setTimeout(() => {
        this.props.afterClose && this.props.afterClose();
      }, 0);
    }
  }

  fixedBody = () => {
    const scrollTop =
      document.body.scrollTop || document.documentElement.scrollTop;
    document.body.style.cssText += `position: fixed; top: -${scrollTop}px; left: 0; right: 0;`;
  };

  // 关闭模态框后调用
  looseBody = () => {
    const body = document.body;
    body.style.position = 'static';
    const top = body.style.top;
    document.body.scrollTop = document.documentElement.scrollTop = -parseInt(
      top as string
    );
    body.style.top = '';
  };

  handleCancel = (evt: React.MouseEvent<HTMLElement>) => {
    getClickPosition(evt);
    const { onCancel } = this.props;
    if (onCancel) {
      onCancel(evt);
    }
  };

  handleOk = (evt: React.MouseEvent<HTMLElement>) => {
    const { onOk } = this.props;
    if (onOk) {
      this.fixedBody();
      onOk(evt);
    }
  };

  modalLocal: IModalLocal = {
    cancelText: '我再想想',
    okText: '立即办理'
  };

  renderModal = () => {
    const {
      title,
      cancelRTBtn,
      visible,
      header,
      children,
      footer,
      okText,
      cancelText,
      ...restProps
    } = this.props;

    const defaultFooter = (
      <div className="modal-footer--list">
        <div
          className="modal-cancel-btn "
          onClick={this.handleCancel}
          {...{ clastag: this.props.oneclstag || '' }}
        >
          {cancelText || this.modalLocal.cancelText}
        </div>
        <div
          className="modal-confirm-btn right-btn"
          onClick={this.handleOk}
          {...{ clastag: this.props.twoclstag || '' }}
        >
          {okText || this.modalLocal.okText}
        </div>
      </div>
    );
    const defaultHeader = (
      <>
        {title ? (
          <p className="modal-header-title" onClick={this.handleCancel}>
            {title}
          </p>
        ) : null}
        {cancelRTBtn && (
          <span
            className="modal-header-cancel"
            onClick={this.handleCancel}
          ></span>
        )}
      </>
    );

    return (
      <>
        <Dialog
          {...restProps}
          visible={visible}
          header={header === undefined ? defaultHeader : header}
          footer={footer === undefined ? defaultFooter : footer}
          children={children}
        />
      </>
    );
  };

  public render() {
    return ReactDom.createPortal(
      <>{this.renderModal()}</>,
      document.getElementById('root') as any
    );
  }
}

// 纯展示组件，不参与处理弹窗逻辑以及兼容问题
const Dialog = (props: IModalProps) => {
  const {
    visible,
    wrapClassName,
    header,
    children,
    footer,
    style = {},
    errorCode
  } = props;

  return visible ? (
    <div className={`modal ${wrapClassName ? wrapClassName : ''}`}>
      <div className="modal-mask"></div>
      <div className={`modal-container`} style={style}>
        <div className="modal-header">{header}</div>
        <div className="modal-body">{children}</div>
        {errorCode ? (
          <div className="modal-errCode">问题码：{errorCode}</div>
        ) : null}
        {footer ? <div className="modal-footer">{footer}</div> : null}
      </div>
    </div>
  ) : null;
};

export default BasicModal;
